/**
 * Created by Administrator on 2017/1/13.
 */

var pomelo = require('pomelo');

require('../../util/class');
var messageService = require('../messageService');

var LogicSubModules = require('../sub');
var replay = require('../cache/replay.js');
var gameUtil = require("../../util/gameUtil");

var SettlementStatus = {
    'NONE': 0,  // 还未结算
    'WAIT': 1,  // 等待结算
    'DONE': 2,  // 结算结算
}

exports.Room = Class.extend({
    _initArgs: null,
    _id: 0,                     // 房间编号
    _creator: 0,               // 房主
    _createTime: 0,           // 创建时间
    _handleTime: 0,           // 上次处理消息的时间
    _nowTick: 0,               // 当前帧（秒）
    _nowTime: 0,               // 当前时间

    _gameType: "",             // 游戏类型
    _subType: "",              // 子游戏类型
    _subConfig: null,         // 子游戏配置
    _options: {},              // 房间选项

    _scene: 0,                 // 场景编号，用于金币场
    _matchId: 0,               // 比赛编号，用于比赛场
    _matchSN: 0,               // 比赛流水，用于比赛场
    _isAgency: false,         // 是否是代开房间

    _round: 0,                 // 总局数(根据局数判断结束)
    _curRound: 0,              // 当前局(根据局数判断结束)
    _costMode: 0,              // 支付方式

    _players: {},               // 每个位置上的玩家，坐位号从1开始
    _playing: false,          // 是否正在游戏，[开始一回合,结束此回合] 这段时间内为 true

    _saveReplay: false,      // 是否保存回放
    _roundReplay: [],         // 本局的战报记录

    _settlementStatus: 0,    // 结算状态

    _destroy: {},              // 销毁房间相关

    _stat: [],                 // 每回合的基础统计


    /**
     * 构造函数
     * @param rid 房间号
     * @param creator 房主
     * @param msg 消息参数
     */
    init: function (rid, creator, msg) {
        var nowStamp = Date.getStamp();

        this._initArgs = msg;
        this._id = rid;
        this._creator = creator;
        this._createTime = nowStamp;
        this._handleTime = nowStamp;
        this._nowTick = 0;
        this._nowTime = nowStamp;

        this._gameType = msg.gameType;
        this._subType = msg.subType;
        this._options = msg.options || {};

        this._scene = msg.scene || 0;
        this._matchId = msg.matchId || 0;
        this._matchSN = msg.matchSN || 0;
        this._isAgency = false;
        if (msg['agency']) {
            this._isAgency = true;
        }

        this._round = msg.round || ExNumber.INT32_MAX;
        this._curRound = 0;
        this._costMode = msg['costMode'] || Def.RoomCostMode.CREATOR;

        this._players = {};
        this._playing = false;

        this._saveReplay = false;
        this._roundReplay = [];

        this._settlementStatus = SettlementStatus.NONE;
        this._destroy = {
            status: false,
            destroyTime: 0,
            reqIndex: 0,
            reqTimer: null,
            reqTime: 0,
            respPlayers: {},
            respNeed: 0,
        };

        this._stat = [];

        var SubLogic = LogicSubModules[this._gameType];
        this._subConfig = SubLogic.Config.SubType[this._subType];
    },

    /**
     * 房间号
     * @returns {number}
     */
    rid: function () {
        return this._id;
    },

    /**
     * 获取房间创建时间
     * @returns {number}
     */
    getCreateTime: function () {
        return this._createTime;
    },
    /**
     * 获取房间初始化参数
     * @returns {*}
     */
    getInitArgs: function () {
        return this._initArgs;
    },
    /**
     * 游戏逻辑模块
     * @returns {*}
     */
    getLogicModule: function () {
        return LogicSubModules[this._gameType];
    },

    /**
     * 获取游戏类型
     * @returns {string}
     */
    getGameType: function () {
        return this._gameType;
    },

    /**
     * 获取游戏配置
     * @returns {*}
     */
    getGameConfig: function () {
        return LogicSubModules[this._gameType].Config;
    },

    /**
     * 获取子游戏类型
     * @returns {string}
     */
    getSubType: function () {
        return this._subType;
    },

    /**
     * 子类型配置
     * @returns {null}
     */
    getSubConfig: function () {
        return this._subConfig;
    },

    /**
     * 场景编号
     * @returns {number}
     */
    getScene: function () {
        return this._scene;
    },

    /**
     * 比赛场编号
     * @returns {number}
     */
    getMatchId: function() {
        return this._matchId;
    },

    /**
     * 场景配置，与subType有关
     * @returns {*}
     */
    getSceneConfig: function () {
        if (this._scene && this._subConfig.scenes) {
            return this._subConfig.scenes[this._scene];
        }

        return null;
    },

    /**
     * 场景基础配置，与subType无关
     * @returns {*}
     */
    getSceneBasicConfig: function () {
        if (!this._scene) {
            return;
        }

        return this.getGameConfig().Scenes[this._scene];
    },

    /**
     * 是否是金币场
     * @returns {boolean}
     */
    isSceneRoom: function () {
        return this._scene ? true : false;
    },

    /**
     * 是否是房卡场
     * @returns {boolean}
     */
    isCardRoom: function () {
        if (this._scene) {
            return false;
        }

        if (this._matchId) {
            return false;
        }

        return true;
    },

    /**
     * 是否是金币场
     */
    isMatchRoom: function() {
        return this._matchId ? true : false;
    },

    /**
     * 是否是代开房间
     * @returns {boolean}
     */
    isAgency: function () {
        return this._isAgency;
    },
    /**
     * 获取隐藏选项
     * @param key
     * @returns {*}
     */
    getHideOption: function (key) {
        var hideOptions = this.getSubConfig().hideOptions;
        return hideOptions ? (hideOptions[key] || 0) : 0;
    },

    /**
     * 获取房间的所有选项
     * @returns {exports.Room._options|{}}
     */
    getOptions: function () {
        return this._options;
    },

    /**
     * 获取创建选项
     * @returns {exports.Room._options|{}}
     */
    getOption: function (key) {
        return this._options[key] || 0;
    },

    /**
     * 获取创建选项，如果创建选项没有从隐藏选项中获取
     * @param key
     * @returns {*}
     */
    getOptionEx: function (key) {
        if (this._options.hasOwnProperty(key)) {
            return this.getOption(key);
        } else {
            return this.getHideOption(key);
        }
    },

    /**
     * 获取房主的uid
     * @returns {number}
     */
    getCreator: function () {
        return this._creator;
    },

    /**
     * 获取支付方式
     * @returns {number}
     */
    getCostMode: function () {
        return this._costMode;
    },

    /**
     * 总局数
     * @returns {number}
     */
    getRound: function () {
        return this._round;
    },

    /**
     * 当前局数
     * @returns {number}
     */
    getCurRound: function () {
        return this._curRound;
    },

    /**
     * 是否正在游戏
     * @returns {boolean}
     */
    isPlaying: function () {
        return this._playing;
    },

    /**
     * 设置是否允许保存回放
     * @param b
     */
    setSaveReplay: function (b) {
        this._saveReplay = b;
        if (b && this.isSceneRoom()) {
            this._saveReplay = false;
        }
    },

    /**
     * 是否允许保存回放
     * @returns {boolean}
     */
    isSaveReplay: function () {
        return this._saveReplay;
    },

    addRoundReplay: function (route, msg) {
        if (this.isSaveReplay()) {
            this._roundReplay.push(route);
            this._roundReplay.push(clone(msg));
        }
    },

    /**
     * 本局是否已经结束
     * @returns {boolean}
     */
    isCurRoundFinished: function () {
        return this._settlementStatus != SettlementStatus.NONE || this.isDestroyed();
    },

    /**
     * 房间是否已经销毁
     * @returns {boolean}
     */
    isDestroyed: function () {
        return this._destroy.status;
    },

    /**
     * 房间销毁时间
     * @returns {number}
     */
    getDestroyTime: function () {
        return this._destroy.destroyTime;
    },

    /**
     * 重置申请销毁房间的请求
     */
    resetReqDestroy: function () {
        if (!this._destroy.reqTimer) {
            return;
        }

        clearTimeout(this._destroy.reqTimer);
        this._destroy.reqTimer = null;
        this._destroy.reqTime = 0;
        this._destroy.respPlayers = {};
        this._destroy.respNeed = 0;
        delete this._destroy.reqIndex;
        delete this._destroy.waitTime;
    },

    /**
     * 添加玩家到某坐位
     * @param index
     * @param player
     */
    addPlayer: function (index, player) {
        this._players[index] = player;
        player.setRoom(this);
        player.setIndex(index);
    },

    /**
     * 根据坐位获取玩家
     * @param index
     * @returns {*}
     */
    getPlayerByIndex: function (index) {
        return this._players[index];
    },

    /**
     * 根据uid获取玩家
     * @param uid
     * @returns {*}
     */
    getPlayerByUid: function (uid) {
        var player = null;
        this.enumPlayers(function (eIndex, ePlayer) {
            if (ePlayer.uid() == uid) {
                player = ePlayer;
                return false;
            }
        });
        return player;
    },

    /**
     * 根据uid获取玩家坐位
     * @param uid
     * @returns {number}
     */
    getPlayerIndex: function (uid) {
        var playerIndex = 0;
        this.enumPlayers(function (eIndex, ePlayer) {
            if (ePlayer.uid() == uid) {
                playerIndex = eIndex;
                return false;
            }
        });
        return playerIndex;
    },

    /**
     * 枚举玩家
     * @param handler(eIndex, ePlayer) 枚举每个人的回调函数
     * @param startIndex 从哪个坐位开始枚举
     */
    enumPlayers: function (handler, startIndex) {
        if (startIndex) {
            for (var i = 0, m = this.getMaxPlayer(); i < m; ++i) {
                var eIndex = (startIndex + i - 1) % m + 1;
                var ePlayer = this._players[eIndex];
                if (ePlayer && (handler(eIndex, ePlayer) === false)) {
                    break;
                }
            }
        } else {
            ExObject.eachKeyNum(this._players, handler);
        }
    },

    /**
     * 枚举在玩游戏的玩家
     * @param handler(eIndex, ePlayer) 枚举每个人的回调函数
     * @param startIndex 从哪个坐位开始枚举
     */
    enumPlaying: function (handler, startIndex) {
        if (startIndex) {
            for (var i = 0, m = this.getMaxPlayer(); i < m; ++i) {
                var eIndex = (startIndex + i - 1) % m + 1;
                var ePlayer = this._players[eIndex];
                if (ePlayer && ePlayer.isPlaying() && (handler(eIndex, ePlayer) === false)) {
                    break;
                }
            }
        } else {
            this.enumPlayers(function (eIndex, ePlayer) {
                if (ePlayer.isPlaying()) {
                    handler(eIndex, ePlayer);
                }
            });
        }
    },

    /**
     * 枚举已准备的玩家
     * @param handler(eIndex, ePlayer) 枚举每个人的回调函数
     * @param startIndex 从哪个坐位开始枚举
     */
    enumReady: function (handler, startIndex) {
        if (startIndex) {
            for (var i = 0, m = this.getMaxPlayer(); i < m; ++i) {
                var eIndex = (startIndex + i - 1) % m + 1;
                var ePlayer = this._players[eIndex];
                if (ePlayer && ePlayer.isReady() && (handler(eIndex, ePlayer) === false)) {
                    break;
                }
            }
        } else {
            this.enumPlayers(function (eIndex, ePlayer) {
                if (ePlayer.isReady()) {
                    handler(eIndex, ePlayer);
                }
            });
        }
    },

    /**
     * 获取所有玩家的uid数组
     * @returns {Array}
     */
    getUidArray: function () {
        var arr = [];
        this.enumPlayers(function (eIndex, ePlayer) {
            arr.push(ePlayer.uid());
        });
        return arr;
    },

    /**
     * 向房间内玩家广播消息
     * @param route 消息码
     * @param msg 消息内容
     * @param excludes [] 要排除的坐位数组
     */
    broadcast: function (route, msg, excludes) {
        var uids = [];
        this.enumPlayers(function (eIndex, ePlayer) {
            if (!excludes || excludes.length == 0 || (excludes.indexOf(eIndex) == -1)) {
                if (ePlayer.isOnline()) {
                    if (!ePlayer.isRobot()) {
                        uids.push({ uid: ePlayer.uid(), sid: ePlayer.sid() });
                    }
                }
            }
            ePlayer.executeAI(route, msg);
        });

        logger.debug("room=%d broadcast %s %j %j", this.rid(), route, msg, uids);

        if (uids.length > 0) {
            messageService.broadcast(uids, route, msg);
        }
    },

    /**
     * 广播并保存战报
     * @param route
     * @param msg
     */
    broadcastReplay: function (route, msg) {
        this.broadcast(route, msg);
        this.addRoundReplay(route, msg);
    },

    /**
     * 向房间内玩家广播消息
     * @param route 消息码
     * @param msg 消息内容
     * @param ex 扩展选项 {excludes:[], nolog:false, replay:false}
     */
    broadcastEx: function (route, msg, ex) {
        if (ex) {
            var excludes = ex.excludes;

            var uids = [];
            this.enumPlayers(function (eIndex, ePlayer) {
                if (!excludes || excludes.length == 0 || (excludes.indexOf(eIndex) == -1)) {
                    if (ePlayer.isOnline()) {
                        if (!ePlayer.isRobot()) {
                            uids.push({ uid: ePlayer.uid(), sid: ePlayer.sid() });
                        }
                    }
                }
                ePlayer.executeAI(route, msg);
            });

            if (!ex.nolog) {
                logger.debug("room=%d broadcast %s %j %j", this.rid(), route, msg, uids);
            }

            if (uids.length > 0) {
                messageService.broadcast(uids, route, msg);
            }

            if (ex.replay) {
                this.addRoundReplay(route, msg);
            }
        } else {
            this.broadcast(route, msg);
        }
    },

    /**
     * 向某坐位上的玩家发送消息
     * @param index 坐位号
     * @param route 消息码
     * @param msg 消息内容
     */
    sendToIndex: function (index, route, msg) {
        var player = this.getPlayerByIndex(index);
        if (player) {
            player.send(route, msg);
        }
    },
    sendToAgency: function (route, msg) {
        if (this.isAgency()) {
            logger.debug("room=%d creator_uid=%d send %s %j", this.rid(), this.getCreator(), route, msg);
            pomelo.app.rpc.world.userRemote.pushMessage(null,this.getCreator(),route,msg,function(resp){});
        }
    },

    /**
     * 获取房间玩家数量
     * @returns {Number}
     */
    getPlayerCount: function () {
        return Object.keys(this._players).length;
    },

    /**
     * 获取房间真实玩家数量
     * @returns {Number}
     */
    getRealPlayerCount: function () {
        var count = 0;
        this.enumPlayers(function (eIndex, ePlayer) {
            if (!ePlayer.isRobot()) {
                count += 1;
            }
        });
        return count;
    },

    /**
     * 房间在线玩家数量
     * @returns {number}
     */
    getOnlinePlayerCount: function () {
        var count = 0;
        this.enumPlayers(function (eIndex, ePlayer) {
            if (ePlayer.isOnline()) {
                count += 1;
            }
        });
        return count;
    },

    /**
     * 房间已准备的玩家数量
     * @returns {number}
     */
    getReadyPlayerCount: function () {
        var count = 0;
        this.enumPlayers(function (eIndex, ePlayer) {
            if (ePlayer.isReady()) {
                count += 1;
            }
        });
        return count;
    },

    /**
     * 房间正在游戏的玩家数量
     * @returns {number}
     */
    getPlayingPlayerCount: function () {
        var count = 0;
        this.enumPlayers(function (eIndex, ePlayer) {
            if (ePlayer.isPlaying()) {
                count += 1;
            }
        });
        return count;
    },

    /**
     * 计算两坐位间的距离
     * @param fromIndex
     * @param toIndex
     */
    getIndexDistance: function (fromIndex, toIndex) {
        var distance = toIndex - fromIndex;
        if (distance < 0) {
            distance += this.getMaxPlayer();
        }
        return distance;
    },

    /**
     * 下一个坐位
     * @param fromIndex
     */
    getNextIndex: function (fromIndex) {
        return fromIndex % this.getMaxPlayer() + 1;
    },

    /**
     * 上一个坐位
     * @param fromIndex
     */
    getPrevIndex: function(fromIndex) {
        var idx = (fromIndex - 1) % this.getMaxPlayer();
        if (idx == 0) {
            idx = this.getMaxPlayer();
        }
        return idx;
    },

    //获取下一个玩家
    getNextPlaying: function (fromIndex) {
        if (fromIndex < 1 || fromIndex > this.getMaxPlayer()) {
            fromIndex = 1;
        }
        var tmpIndex = fromIndex;
        do {
            tmpIndex = this.getNextIndex(tmpIndex);
            var tmpPlayer = this.getPlayerByIndex(tmpIndex);
            if (tmpPlayer && tmpPlayer.isPlaying()) {
                break;
            }
        } while (tmpIndex != fromIndex);

        return tmpIndex;
    },
    getNextPlayer: function (fromIndex) {
        if (fromIndex < 1 || fromIndex > this.getMaxPlayer()) {
            fromIndex = 1;
        }
        var tmpIndex = fromIndex;
        do {
            tmpIndex = this.getNextIndex(tmpIndex);
            var tmpPlayer = this.getPlayerByIndex(tmpIndex);
            if (tmpPlayer) {
                break;
            }
        } while (tmpIndex != fromIndex);

        return tmpIndex;
    },

    /**
     * 尝试开始新一回合(局)，将先检查是否可以开始新一局，然后再尝试开始
     */
    tryStartNewRound: function () {
        if (this.checkStartNewRound()) {
            this._startNewRound();
        }
    },

    /**
     * 结算当前回合，先结算再结束
     * @param waitMS 等待时长(毫秒)
     */
    settlementCurRound: function (waitMS) {
        if (!this.isPlaying()) {
            return;
        }

        if (this._settlementStatus != SettlementStatus.NONE) {
            return;
        }

        this._settlementStatus = SettlementStatus.WAIT;
        if (waitMS) {
            setTimeout(function () {
                this._settlement();
            }.bind(this), waitMS);
        } else {
            this._settlement();
        }
    },

    /**
     * 结束当前回合
     */
    finishCurRound: function () {
        this._playing = false;
        var roomConfig = this.getRoomConfig();
        if(roomConfig.resetDestroyOnNewRound){
            this.resetReqDestroy();

        }
        this.enumPlaying(function (eIndex, ePlayer) {
            ePlayer.setPlaying(false);
            ePlayer.setReady(false);
        });

        this.onCurRoundFinished();

        if (this.isSaveReplay()) {
            replay.addRoundReplay(this.rid(), this.getCurRound(), this._roundReplay);
            this._roundReplay = [];
        }

        this.enumPlayers(function (eIndex, ePlayer) {
            ePlayer.onCurRoundFinished();
        });

        if (this.isFinalRound()) {
            this.destroy();
        }
    },

    /**
     * 销毁房间
     */
    destroy: function () {
        if (this.isDestroyed()) {
            return;
        }

        this._destroy.status = true;
        this._destroy.destroyTime = Date.getStamp();

        this.onDestroy();

        var roomConfig = this.getRoomConfig();
        if (roomConfig.destroySettlement) {
            if (this.isPlaying()) {
                if (roomConfig.destroySettlement == Def.DestroySettlement.YES) {
                    this.settlementCurRound(0);
                } else {
                    this.broadcast("onRoundSettlement", this.getSettlementInfo());
                }
            } else {
                if (!this.isFinalRound() && this._curRound > 0) {
                    this.broadcast("onRoundSettlement", this.getSettlementInfo());
                }
            }
        }

        var roomId = this.rid();
        var uidArray = this.getUidArray();

        this.broadcast("onRoomFinished", {
            roomId: roomId
        });

        pomelo.app.rpc.world.roomRemote.destroyRoom(null, {
            roomId: roomId,
            creator: this.getCreator(),
            uidArray: uidArray
        }, function () {
        });

        if (this.isCardRoom() && uidArray.length > 0) {
            var costResource = this.calcCostResource();
            if (costResource) {
                ExObject.minus(costResource);

                switch (this._costMode) {
                    case Def.RoomCostMode.CREATOR:
                        pomelo.app.rpc.world.userRemote.alterResource(null, this.getCreator(), costResource, "roomFinished", function () {
                        });
                        break;
                    case Def.RoomCostMode.AA:
                        var resources = {};
                        uidArray.forEach(function (uid) {
                            resources[uid] = costResource;
                        });

                        pomelo.app.rpc.world.userRemote.batchAlterResource(null, resources, "roomFinished", function (resp) {
                        });
                        break;
                    case Def.RoomCostMode.WINNER:
                        var winner = {
                            'uid': 0,
                            'score': 0
                        }
                        this.enumPlayers(function (eIndex, ePlayer) {
                            if (ePlayer.getScore() >= winner.score) {
                                winner.score = ePlayer.getScore();
                                winner.uid = ePlayer.uid();
                            }
                        });

                        pomelo.app.rpc.world.userRemote.alterResource(null, winner['uid'], costResource, "roomFinished", function () {
                        });
                        break;
                    case Def.RoomCostMode.ALL:
                        var resources = {};
                        uidArray.forEach(function (uid) {
                            resources[uid] = costResource;
                        });
                        pomelo.app.rpc.world.userRemote.batchAlterResource(null, resources, "roomFinished", function (resp) {
                        });
                    default:
                        break;
                }
            }

            if (this._curRound > 0) {
                var report = this.getFinalReportInfo();
                replay.addReport(roomId, report, function (err) {
                    if (err) {
                        return;
                    }

                    pomelo.app.rpc.world.reportRemote.addReport(null, roomId, this.getUidArray(), function () {});
                }.bind(this));

                pomelo.app.rpc.world.roomRemote.addCardRoomStat(null, report, function() {});
            }
        }

        if (this.isMatchRoom()) {
            pomelo.app.rpc.world.roomRemote.updateMatchResult(null, this.getMatchResultInfo(), function () {});
        }
    },

    /**
     * 处理每个消息前调用此接口
     * @param name 接口名称
     */
    beforeHandle: function (name) {
        this._handleTime = Date.getStamp();
    },

    /**
     * 每秒钟定时
     */
    tick: function () {
        this._nowTick++;
        this._nowTime = Date.getStamp();

        // 无玩家在线且一定时间内没有收到任何消息将销毁房间
        if ((this.getOnlinePlayerCount() == 0) && (this._nowTime > this._handleTime + 3600)) {
            this.destroy();
            return;
        }

        // 比赛场创建房间后一定时间内人数不齐将将销毁房间
        if (this.isMatchRoom() && (this.getPlayerCount() < this.getMaxPlayer()) && (this._nowTime > this._createTime + 60)) {
            this.destroy();
            return;
        }

        this.onTick();
    },

    getNowTick: function () {
        return this._nowTick;
    },

    getNowTime: function () {
        return this._nowTime;
    },

    /******************************************************************
     * 需要重载的接口
     ******************************************************************/

    /**
     * 创建房间时检查
     */
    checkInit: function () {
        return true;
    },

    /**
     * 每个房间最大人数
     */
    getMaxPlayer: function () {
        return gameUtil.calcRoomMaxPlayer(this.getSubConfig(), this._initArgs);
    },

    /**
     * 分配坐位号
     * @param uid 要分配坐位的玩家uid
     */
    allocateIndex: function (uid) {
        if (this.isCardRoom() && this._creator && !this.isAgency()) { // 房卡场房主坐1号位
            if (uid == this._creator) {
                return 1;
            }

            for (var i = 2, m = this.getMaxPlayer(); i <= m; ++i) {
                if (!this._players[i]) {
                    return i;
                }
            }
        } else {
            for (var i = 1, m = this.getMaxPlayer(); i <= m; ++i) {
                if (!this._players[i]) {
                    return i;
                }
            }
        }

        return 0;
    },

    /**
     * 获取房间配置
     */
    getRoomConfig: function () {
        return {
            'destroyWaitTime': 300,          // 申请解散房间的等待时长
            'destroySettlement': Def.DestroySettlement.SEND,// 解散房间是否结算，参考Def.DestroySettlement
            'waitTimeoutTime': 0,            // 等待响应超时时长(超时将被算作强退)
            'waitTimeoutAfter': 5,           // 等待响应超时后定时时长
            'waitTimeoutTrustee': 0,         // 等待响应超时后是否托管
            'respDestroyIgnore': 0,          // 响应解散房间的忽略人数（X人房间Y人同意即可解散，忽略人数为X-Y）
            'respDestroyIgnoreOffline': 1,   // 响应解散房间是否忽略离线玩家
            'respDestroyReject': 1,          // 响应解散房间的拒绝人数（拒绝达到多少人将直接解散）
            'resetDestroyOnNewRound': 1,     // 房间结算时是否重置解散房间信息
            'enableJoinAfterStart': false   // 是否允许中途加入
        }
    },

    /**
     * 是否是最后一回合
     * @returns {*|boolean}
     */
    isFinalRound: function () {
        return this._curRound >= this._round;
    },

    /**
     * 房间信息
     */
    getInfo: function () {
        var info = {
            roomId: this.rid(),
            gameType: this.getGameType(),
            subType: this.getSubType(),
            scene: this.getScene(),
            matchId: this.getMatchId(),
            options: this.getOptions(),
            round: this._round,
            curRound: this._curRound,
            creator: this.getCreator(),
            playing: this._playing,
            players: {},
        };

        this.enumPlayers(function (eIndex, ePlayer) {
            info.players[eIndex] = ePlayer.getInfo();
        });

        return info;
    },

    /**
     * 本局基础信息，每一局开始时会将此信息广播给客户端
     * @returns {{curRound: number}}
     */
    getRoundInfo: function () {
        return {
            curRound: this._curRound
        }
    },

    /**
     * 连线重连信息
     * @param rePlayer
     * @returns {{roomId: *, gameType: *, subType: *, options: *, round: number, curRound: number, playing: boolean, players: {}}}
     */
    getReconnectInfo: function (rePlayer) {
        var info = {
            roomId: this.rid(),
            gameType: this.getGameType(),
            subType: this.getSubType(),
            scene: this.getScene(),
            matchId: this.getMatchId(),
            options: this.getOptions(),
            round: this._round,
            curRound: this._curRound,
            creator: this.getCreator(),
            playing: this._playing,
            players: {},
        };

        this.enumPlayers(function (eIndex, ePlayer) {
            info.players[eIndex] = ePlayer.getReconnectInfo(rePlayer.getIndex() == eIndex);
        });

        if (this._destroy.reqTimer) {
            info.destroyInfo = {
                playerIndex: this._destroy.reqIndex,
                destroyTime: this._destroy.reqTime,
                durationTime: this._destroy.waitTime,
                respPlayers: this._destroy.respPlayers
            }
        }

        return info;
    },

    /**
     * 房间的结算信息
     * @returns {{}}
     */
    getSettlementInfo: function () {
        var info = {
            finalRound: this.isFinalRound(),
            players: {},
        };

        if (this.isDestroyed()) {
            info.destroyRound = true;
        }

        var sceneConfig = this.getSceneConfig();
        if (sceneConfig) {
            info.sceneScoreRate = sceneConfig.scoreRate;
        }

        if (info.finalRound || info.destroyRound) {
            this.enumPlayers(function (eIndex, ePlayer) {
                info.players[eIndex] = ePlayer.getSettlementInfo();
            });
        } else {
            this.enumPlaying(function (eIndex, ePlayer) {
                info.players[eIndex] = ePlayer.getSettlementInfo();
            });
        }


        return info;
    },

    /**
     * 总结算战报信息
     * @returns {{roomId: (*|number), createTime: (*|number), creator: (*|number), gameType: (*|string), players: {}}}
     */
    getFinalReportInfo: function () {
        var info = {
            roomId: this.rid(),
            curRound: this.getCurRound(),
            createTime: this.getCreateTime(),
            creator: this.getCreator(),
            gameType: this.getGameType(),
            subType: this.getSubType(),
            players: {},
            stat: this._stat
        }

        this.enumPlayers(function (eIndex, ePlayer) {
            info.players[eIndex] = ePlayer.getFinalReportInfo();
        });

        return info;
    },

    getMatchResultInfo: function() {
        var info = {
            roomId: this.rid(),
            matchSN: this._matchSN,
            players: {}
        }

        if (this.getPlayerCount() < this.getMaxPlayer()) {
            this.enumPlayers(function (eIndex, ePlayer) {
                ePlayer.addRoundScore(1);
            });
        }

        this.enumPlayers(function (eIndex, ePlayer) {
            info.players[ePlayer.uid()] = ePlayer.getMatchResultInfo();
        });

        return info;
    },

    /**
     * 计算消耗的资源
     * @returns {*}
     */
    calcCostResource: function () {
        if (this._curRound < 1 || (this._curRound == 1 && this.isPlaying())) {
            return null;
        }

        if (this._initArgs.needResourceRate === 0) {
            return null;
        }

        return gameUtil.calcCreateRoomNeedResource(this.getLogicModule(), this.getInitArgs());
    },

    /**
     * 检查是否可开始新的一局
     * @returns {boolean}
     */
    checkStartNewRound: function () {
        if (this.isPlaying()) {
            return false;
        }

        if (this.getReadyPlayerCount() >= this.getMaxPlayer()) {
            return true;
        }

        return false;
    },

    /**
     * 每秒钟的定时处理
     */
    onTick: function () {
        if (!this.isSceneRoom() && !this.isMatchRoom()) {
            return;
        }

        var now = this.getNowTime();
        var roomConfig = this.getRoomConfig();

        if (roomConfig.waitTimeoutTime > 0) {
            this.enumPlayers(function (eIndex, ePlayer) {
                var waitTime = ePlayer.getWaitHandleTime();
                if (!ePlayer.isWaitTimeout() && waitTime && (now - waitTime) > roomConfig.waitTimeoutTime) {

                    if (roomConfig.waitTimeoutAfter) {
                        this.broadcast("onPlayerSetWaitTimeout", {
                            playerIndex: eIndex,
                            time: roomConfig.waitTimeoutAfter,
                        });

                        ePlayer.setWaitTimeout(function () {
                            ePlayer.setMark('forceQuit', true);
                            this._playerQuitRoom(ePlayer);
                        }.bind(this), roomConfig.waitTimeoutAfter * 1000);
                    } else if (roomConfig.waitTimeoutTrustee) {
                        ePlayer.setWaitTimeout(function () {
                            ePlayer.setTrustee(roomConfig.waitTimeoutTrustee);
                        }.bind(this));
                    }

                }
            }.bind(this));
        }
    },

    /**
     * 有玩家断线重连
     * @param player
     */
    onPlayerReconnect: function (player) {

    },

    /**
     * 有玩家进入房间
     * @param player
     */
    onPlayerEnter: function (player) {

    },

    /**
     * 有玩家准备前，检查是否满足条件
     * @param player
     * @returns {number}
     */
    onBeforePlayerReady: function (player) {
        if (this.isSceneRoom()) {
            return gameUtil.checkSceneRoomCoin(this.getSceneConfig(), player.getResource('coin'));
        }
        return Code.SUCCESS;
    },

    /**
     * 有玩家准备后
     * @param player
     */
    onAfterPlayerReady: function (player) {

    },


    /**
     * 有玩家退出前，检查是否允许退出
     * @param player
     * @returns {number}
     */
    onBeforePlayerQuitRoom: function (player, msg) {
        if (this.isPlaying() && !msg.forceQuit) {
            return Code.ROOM_PLAYING;
        }

        if (this.isCardRoom()) {
            if (this.getCurRound() > 0) { //房卡场默认 开始之后就不能再退出
                return Code.ROOM_STARTED;
            }
        }

        return Code.SUCCESS;
    },

    /**
     * 有玩家退出房间
     * @param player
     */
    onPlayerQuitRoom: function (player) {
        if (player.getMark('forceQuit')) {
            if (this.isPlaying() && !this.isCurRoundFinished()) {
                this.settlementCurRound(0);
            }
        }
    },
    /**
     * 玩家退出房间后
     * @param player
     */
    onAfterPlayerQuitRoom:function(player){

    },

    /**
     * 一回合开始之前
     */
    onBeforeStartNewRound: function () {
        this._settlementStatus = SettlementStatus.NONE;
    },

    /**
     * 一回合开始之后
     */
    onAfterStartNewRound: function () {

    },

    /**
     * 本回合结算处理
     */
    onSettlement: function () {
    },

    /**
     * 金币场结算处理
     */
    onSettlementSceneRoom: function () {
        var sceneConfig = this.getSceneConfig();

        var values = {};
        var totalLost = 0;
        var totalWin = 0;
        var scale = 1;
        var totalForceQuit = 0;
        var playerCount = 0;

        this.enumPlaying(function (eIndex, ePlayer) {
            var value = 0;
            if (ePlayer.getMark("forceQuit")) {
                value = Math.max(-ePlayer.getResource('coin'), -sceneConfig.forceQuitCoin);
                totalForceQuit += Math.abs(value);
            } else {
                playerCount++;
                value = ePlayer.getRoundScore() * sceneConfig.scoreRate;
                if (value > 0) {
                    totalWin += value;
                } else {
                    value = Math.max(-ePlayer.getResource('coin'), value);
                    totalLost += Math.abs(value);
                }
            }

            values[eIndex] = value;
        }.bind(this));

        if (totalLost < totalWin) {
            scale = totalLost / totalWin;
        }

        this.enumPlaying(function (eIndex, ePlayer) {
            var value = values[eIndex];

            if (ePlayer.getMark("forceQuit")) {
                //
            } else {
                if (value > 0) {
                    value = Math.round(values[eIndex] * scale);
                }

                value += Math.round(totalForceQuit / playerCount);
            }

            ePlayer.addRoundCoin(value);
        });
    },

    /**
     * 比赛场结算处理
     */
    onSettlementMatchRoom: function() {
        var forceQuitCount = 0;
        this.enumPlaying(function (eIndex, ePlayer) {
            if (ePlayer.getMark("forceQuit")) {
                forceQuitCount++;
            }
        }.bind(this));

        if (forceQuitCount > 0) {
            this.enumPlaying(function (eIndex, ePlayer) {
                if (ePlayer.getMark("forceQuit")) {
                    // nothing
                } else {
                    ePlayer.addRoundScore(1);
                }
            }.bind(this));
        }
    },

    /**
     * 本回合结束
     */
    onCurRoundFinished: function () {

    },

    /**
     * 房间销毁
     */
    onDestroy: function () {

    },


    /******************************************************************
     * 消息处理接口
     ******************************************************************/

    /**
     * 玩家断线重连
     * @param sid 前端服务器id
     * @param player 玩家对象
     * @param callback(code, roomInfo, playerInfo)
     */
    handlePlayerReconnect: function (sid, player, callback) {
        player.setOnline(sid);
        player.setBackground(false);

        this.onPlayerReconnect(player);

        var playerIndex = player.getIndex();
        var reconnectInfo = {
            roomInfo: this.getReconnectInfo(player),
            playerInfo: { playerIndex: playerIndex },
            reconnect: true
        };
        callback(Code.SUCCESS, reconnectInfo);
        logger.info("room=%d index=%d uid=%d reconnect=%j", this.rid(), player.getIndex(), player.uid(), reconnectInfo);

        player.send("onPlayerEnterResponse", reconnectInfo);

        this.broadcast("onPlayerReconnect", {
            playerIndex: playerIndex,
            player: player.getInfo(),
        }, [playerIndex]);
    },

    /**
     * 玩家进入房间
     * @param sid 前端服务器id
     * @param player 玩家对象
     * @param callback(code, roomInfo, playerInfo)
     */
    handlePlayerEnter: function (sid, player, callback) {
        player.setOnline(sid);
        player.setBackground(false);

        this.onPlayerEnter(player);

        var playerIndex = player.getIndex();
        var enterInfo = {
            roomInfo: this.getInfo(player),
            playerInfo: { playerIndex: playerIndex },
            reconnect: false
        };
        callback(Code.SUCCESS, enterInfo);
        logger.info("room=%d index=%d uid=%d enter=%j", this.rid(), player.getIndex(), player.uid(), enterInfo);

        player.send("onPlayerEnterResponse", enterInfo);

        this.broadcast("onPlayerEnter", {
            playerIndex: playerIndex,
            player: player.getInfo(),
        }, [playerIndex]);

        this.tryStartNewRound();
    },

    handlePlayerOffline: function (player) {
        player.setOnline(null);

        logger.debug("room=%d index=%d uid=%d offline game", this.rid(), player.getIndex(), player.uid());
        this.broadcast("onPlayerOffline", {
            playerIndex: player.getIndex(),
        });
    },

    handlePlayerReady: function (player, msg, callback) {
        if (player.isReady()) {
            callback({ code: Code.SUCCESS });
            return;
        }

        var code = this.onBeforePlayerReady(player);
        if (code) {
            callback({ code: code });
            return;
        }

        this.broadcast("onPlayerReady", {
            playerIndex: player.getIndex(),
        });

        callback({});

        player.setReady(true);
        this.onAfterPlayerReady(player);

        this.tryStartNewRound();
    },

    handleDestroyRoom: function (player, msg, callback) {
        if (this.getCurRound() > 0 || this.isPlaying()) {
            callback({ code: Code.ROOM_PLAYING });
            return;
        }

        if (player.uid() != this.getCreator()) {
            callback({ code: Code.NOT_ROOM_CREATOR });
            return;
        }

        callback({});

        this.destroy();
    },

    handleReqDestroyRoom: function (player, msg, callback) {
        callback({});

        if (this._destroy.reqTimer) {
            return;
        }

        var playerIndex = player.getIndex();
        var roomConfig = this.getRoomConfig();

        var respNeed = (roomConfig.respDestroyIgnoreOffline) ? this.getOnlinePlayerCount() : this.getPlayerCount();
        if (respNeed <= 1) {
            this.destroy();
            return;
        }

        this._destroy.reqTime = Date.getStamp();
        this._destroy.respPlayers = {};
        this._destroy.respPlayers[playerIndex] = 1;
        this._destroy.respNeed = (respNeed >= 3) ? respNeed - roomConfig.respDestroyIgnore : respNeed;
        this._destroy.reqIndex = playerIndex;
        this._destroy.waitTime = roomConfig.destroyWaitTime;
        this._destroy.reqTimer = setTimeout(function () {
            this.destroy();
        }.bind(this), roomConfig.destroyWaitTime * 1000);

        var destroyInfo = {
            playerIndex: playerIndex,
            destroyTime: this._destroy.reqTime,
            durationTime: roomConfig.destroyWaitTime
        };

        this.broadcast("onReqDestroyRoom", destroyInfo);
    },

    handleRespDestroyRoom: function (player, msg, callback) {
        callback({});

        if (!this._destroy.reqTimer) {
            return;
        }

        if (this._destroy.respPlayers.hasOwnProperty(player.getIndex())) {
            return;
        }

        var ok = msg.ok;
        this.broadcast("onRespDestroyRoom", {
            playerIndex: player.getIndex(),
            ok: ok
        });

        if (ok) {
            this._destroy.respPlayers[player.getIndex()] = 1;
            var okNum = ExObject.countEQ(this._destroy.respPlayers, 1);
            if (okNum >= this._destroy.respNeed) {
                this.resetReqDestroy();
                this.destroy();
            }
        } else {
            this._destroy.respPlayers[player.getIndex()] = 0;
            if (ExObject.countEQ(this._destroy.respPlayers, 0) >= (this.getRoomConfig().respDestroyReject || 1)) {
                this.resetReqDestroy();

                this.broadcast("onResetReqDestroy", {});
            }
        }
    },

    handleQuitRoom: function (player, msg, callback) {
        var code = this.onBeforePlayerQuitRoom(player, msg);
        if (code) {
            callback({ code: code });
            return;
        }
        callback({});

        if (player.isPlaying()) {
            player.setMark('forceQuit', true);
        }

        this._playerQuitRoom(player);
    },

    /**
     * 强退
     * @param player
     * @param msg
     * @param callback
     */
    handleForceQuitRoom: function (player, msg, callback) {
        msg.forceQuit = true;
        this.handleQuitRoom(player, msg, callback);
    },

    handleChat: function (player, msg, callback) {
        msg.nolog = true;
        var message = msg.message;
        if (!message) {
            callback({ code: Code.ARGS_INVALID });
            return;
        }

        callback({});

        this.broadcastEx("onRoomChat", {
            playerIndex: player.getIndex(),
            message: message
        }, { nolog: true });
    },

    handleSetBackground: function (player, msg, callback) {
        var bg = msg.bg;
        player.setBackground(bg);
        callback({});

        this.broadcast("onPlayerBgChanged", {
            playerIndex: player.getIndex(),
            bg: bg
        });
    },

    callHandler: function (name, player, msg, callback) {
        var handler = this[name];
        if (!handler) {
            callback && callback({ code: Code.GAME_ROUTE_INVALID });
            return;
        }

        this.beforeHandle(name);
        handler.call(this, player, msg, function (resp) {
            logger.debug("room=%d index=%d %s msg=%j resp=%j", this.rid(), player.getIndex(), name, msg, resp);
            callback && callback(resp);
        }.bind(this));
    },

    /******************************************************************
     * 私有功能接口
     ******************************************************************/

    _startNewRound: function () {
        if (this.isFinalRound()) {
            return;
        }

        if (this.isDestroyed()) {
            return;
        }

        if (this.isPlaying()) {
            return;
        }

        // 初始化游戏数据
        this._playing = true;
        this._curRound += 1;

        //setTimeout(function () {
        //    this._doStartNewRound();
        //}.bind(this), 500);
        this._doStartNewRound();
    },

    _doStartNewRound: function () {
        this.onBeforeStartNewRound();

        // 广播房间状态
        this.addRoundReplay("onRoomInfo", this.getInfo());
        this.broadcastReplay("onStartNewRound", this.getRoundInfo());

        this.enumPlayers(function (eIndex, ePlayer) {
            if (ePlayer.isReady()) {
                ePlayer.setPlaying(true);
                ePlayer.onStartNewRound();
            }
        });

        this.onAfterStartNewRound();
    },


    _playerQuitRoom: function (player) {
        this.onPlayerQuitRoom(player);

        delete this._players[player.getIndex()];

        this.broadcast("onPlayerQuitRoom", {
            playerIndex: player.getIndex(),
            uid: player.uid()
        });

        player.send("onRoomFinished", {
            roomId: this.rid()
        });

        pomelo.app.rpc.world.roomRemote.quitRoom(null, {
            uid: player.uid(),
        }, function () {
        });
        this.onAfterPlayerQuitRoom(player);

        if (this.isMatchRoom()) {
            this.destroy();
        } else {
            if (this.getPlayerCount() < 1 && !this.isAgency()) {
                this.destroy();
            }
        }
    },

    _settlement: function () {
        if (!this.isPlaying()) {
            return;
        }

        if (this._settlementStatus != SettlementStatus.WAIT) {
            return;
        }

        this._settlementStatus = SettlementStatus.DONE;

        this.onSettlement();

        this.enumPlaying(function (eIndex, ePlayer) {
            ePlayer.settlement();
        });

        if (this.isCardRoom()) {
            var roundStat = {};
            this.enumPlaying(function (eIndex, ePlayer) {
                roundStat[eIndex] = ePlayer.getRoundScore();
            });
            this._stat.push(roundStat);
        } else if (this.isSceneRoom()) {
            this.onSettlementSceneRoom();
        } else if (this.isMatchRoom()) {
            this.onSettlementMatchRoom();
        }


        this.broadcast("onRoundSettlement", this.getSettlementInfo());

        if (this.isSceneRoom()) {
            this._updateSceneSettlement();
        }

        this.finishCurRound();
    },

    _updateSceneSettlement: function () {
        var sceneConfig = this.getSceneConfig();
        setTimeout(function () {
            this.enumPlayers(function (eIndex, ePlayer) {
                var code = gameUtil.checkSceneRoomCoin(sceneConfig, ePlayer.getResource('coin'));
                if (code) {
                    ePlayer.send("onPlayerQuitRoomWithCode", {
                        playerIndex: ePlayer.getIndex(),
                        uid: ePlayer.uid(),
                        code: code
                    });

                    room._playerQuitRoom(ePlayer);
                }
            }.bind(this));
        }.bind(this), 1000);

        var updateInfo = {
            gameType: this.getGameType(),
            subType: this.getSubType(),
            scene: this.getScene(),
            players: {}
        };

        var syncCoins = {};
        this.enumPlaying(function (eIndex, ePlayer) {
            var coin = ePlayer.alterResource('coin', ePlayer.getRoundCoin() - sceneConfig.roundCoin);
            updateInfo.players[ePlayer.uid()] = { score: ePlayer.getRoundScore(), coin: coin };
            syncCoins[ePlayer.uid()] = ePlayer.getResource('coin');
        });
        this.broadcast("onGameSyncCoins", syncCoins);

        var room = this;
        pomelo.app.rpc.world.roomRemote.updateSceneSettlement(null, updateInfo, function (respUsers) {
            logger.debug("room=%d updateSceneSettlement info=%j resp=%j", room.rid(), updateInfo, respUsers);
            if (!respUsers) {
                return;
            }

            ExObject.eachKeyNum(respUsers, function (uid, respUser) {
                var player = room.getPlayerByUid(uid);
                if (player) {
                    player.updateUser(respUser);
                }
            });
        }.bind(this));
    }
});
